package com.espirit.eap.conversion;

import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.espirit.eap.pagelayout.BlockSection;
import com.espirit.eap.pagelayout.Element;
import com.espirit.eap.pagelayout.ElementService.ElementCallback;
import com.espirit.eap.pagelayout.Page;
import com.espirit.eap.pagelayout.TagInfo;
import com.espirit.eap.util.TaglibManager;

public class PrePageConverterCallback  implements ElementCallback {

	private static final Log logger = LogFactory.getLog(PrePageConverterCallback.class);
	
	private TaglibManager tagManager;
	private Page pageSetting;
	private ConversionContext conversionContext;
	
	public PrePageConverterCallback(ConversionContext conversionContext) {
		this.conversionContext = conversionContext;
		this.pageSetting = conversionContext.getPage();
		this.tagManager = conversionContext.getTagManager();
	}
	
	@Override
	public boolean process(Element parent, Element element, int deep) {
		return checkTagCategory(parent, element);
	}
	
	private Collection<String> coreTags = new HashSet<>(Arrays.asList(
			"inject", "header", "footer", "include", "code", "css", "script", "layout", "template",
			"datasource", "condition", "form"));
	
	private boolean isCoreTag(String tagName) {
		return coreTags.contains(tagName);
	}
	
	private boolean checkTagCategory(Element parent, Element element) {
		boolean checkChild = true;
		String tagName = element.getTagName();
		
		// check & add the category style class
		if (tagName != null) {
			TagInfo tagInfo = tagManager.getDefaultTagInfo(pageSetting.getLayout().getCategories(), tagName);
			if (tagInfo != null && tagInfo.hasTagProperty("clazz")) {
				String category = tagInfo.getTagCategory().getName();
				boolean isExcludeCategory = excludeCategory(element, category);
				if (isExcludeCategory) {
					checkChild = false;
				}
				
				// core ftl doesn't need the style class (or just filter the 'layout' and 'form'?
				if (!isExcludeCategory && !isCoreTag(tagInfo.getTagName())) {
					// TODO: use better strategy? such the maxinum share parent
					BlockSection cssContainer = getClosestCssContainer(parent);
					String categoryCssFlag = '_' + category;
					if (cssContainer != null) {
						boolean hasCssStyleDefinition = hasCssStyleDefinition(cssContainer, categoryCssFlag);
						if (!hasCssStyleDefinition) {
							conversionContext.addCategoryClass(cssContainer, categoryCssFlag);
							// TODO: add exclude category to sub element? footer script
						}
					} else {
						conversionContext.addCategoryClass(pageSetting, categoryCssFlag);
						// TODO: add exclude category to sub element? footer script
						// <script>addCssNotFlag(subElementId, '_' + categoryCssFlag)</script>
					}
					
					String clazz = element.getExProperty("clazz");
					int index;
					if (clazz != null && (index = clazz.indexOf("__")) >= 0) {
						int end = clazz.indexOf(' ', index + 2);
						String exClass = end > 0 ? clazz.substring(index, end) : clazz.substring(index);
						conversionContext.addExCategoryClass(element, exClass);
					}
				}
			}
		}
		return checkChild;
	}
	
	private boolean excludeCategory(Element element, String category) {
		String clazz = element.getExProperty("clazz");
		if (clazz != null && clazz.indexOf("__" + category) != -1) {
			return true;
		} else {
			return false;
		}
	}
	
	private BlockSection getClosestCssContainer(Element parent) {
		while (parent != null) {
			if (parent instanceof BlockSection && (
					"layout".equals(parent.getTagName()) || "container".equals(parent.getTagName()))) {
				return (BlockSection) parent;
			}
			parent = parent.getParent();
		}
		return null;
	}
	
	private boolean hasCssStyleDefinition(BlockSection cssContainer, String category) {
		while (cssContainer != null) {
			String clazz = cssContainer.getExProperty("clazz");
			boolean hasDefinition = clazz != null && (" " + clazz).indexOf(category) != -1;
			if (hasDefinition) {
				return true;
			}
			String additionClazz = conversionContext.getCategoryClass(cssContainer);
			hasDefinition = additionClazz != null && (" " + additionClazz).indexOf(category) != -1;
			if (hasDefinition) {
				return true;
			}
			
			BlockSection parent = (BlockSection) cssContainer.getParent();
			if (parent != cssContainer) {
				cssContainer = parent;
			} else {
				logger.error("Stop check, found dead-cycle definition: " + cssContainer.getName());
				break;
			}
		}
		return false;
	}
}